//
//  PSTrick.m
//  PrefCore
//
//  Created by Sergey on 2/10/14.
//  Copyright (c) 2014 Play-on-smart. All rights reserved.
//

#import "PSTrick.h"

#define MASK_CARD 0x3F

// Private helper functions

/// Increment the number of cards in a trick.
static inline PSTrickHash PSTrickIncrement(PSTrickHash trick) {
    return trick + (1U << 30);
}

/// Decrement the number of cards in a trick.
static inline PSTrickHash PSTrickDecrement(PSTrickHash trick) {
    return trick - (1U << 30);
}

/// Determines the highest card in this trick.
static inline PSCard* PSTrickHighestWithTrump(PSCard* c1, PSCard* c2, PSCardSuit trump) {
    if (c1.suit == c2.suit) {
        return (c1.rank > c2.rank ? c1 : c2);
    }
    return c2.suit == trump ? c2 : c1;
}

static inline int PSTrickDecodeOwner(uint8_t encoded) {
    return ((encoded >> 6) & 0x03);
}

/// Shift values for the card order.
static int shifts[] = {16, 8, 0, 24};

static inline uint8_t PSTrickCardRep(PSTrickHash trick, int index) {
    return (uint8_t)((trick >> shifts[index]) & 0xFF);
}

static inline void PSTrickAppendCard(NSMutableString *res, uint8_t encoded) {
    if (!encoded) {
        [res appendString:@"-"];
        return;
    }
    [res appendFormat:@"%d:%@", PSTrickDecodeOwner(encoded), [PSCard cardWithIndex:encoded & MASK_CARD]];
}

// Public functions

int PSTrickSize(PSTrickHash trick) {
    return trick >> 30;
}

PSTrickHash PSTrickAddCardTalon(PSTrickHash trick, PSCardIndex card) {
    return trick | (((PSTrickHash)card) << 24);
}

PSTrickHash PSTrickAddCardPlayer(PSTrickHash trick, int player, PSCardIndex card) {
    int shift = shifts[PSTrickSize(trick)];
    return PSTrickIncrement(trick) | ((PSTrickHash)card) << shift | player << (shift + 6);
}

PSTrickHash PSTrickRemoveLastCard(PSTrickHash trick) {
    int size = PSTrickSize(trick);
    assert(size);
    return PSTrickDecrement(trick & ~(0xFF << shifts[size-1]));
}

PSCard *PSTrickTalonCard(PSTrickHash trick) {
    return PSTrickCard(trick, 3);
}

PSCard *PSTrickCard(PSTrickHash trick, int index) {
    return [PSCard cardWithIndex:(trick >> shifts[index]) & MASK_CARD];
}

int PSTrickCardOwner(PSTrickHash trick, int index) {
    return PSTrickDecodeOwner((trick >> shifts[index]) & 0xFF);
}

PSCard *PSTrickCardOfPlayer(PSTrickHash trick, PSPlayer *player) {
    int size = PSTrickSize(trick);
    for (int i = 0; i != size; i++) {
        if (PSTrickCardOwner(trick, i) == player.identifier) {
            return PSTrickCard(trick, i);
        }
    }
    return nil;
}

PSCardSuit PSTrickCardSuit(PSTrickHash trick) {
    PSCard *talon = PSTrickTalonCard(trick);
    if (talon) {
        return talon.suit;
    }
    PSCard *first = PSTrickCard(trick, 0);
    return first ? first.suit : kSuitNone;
}

PSCard *PSTrickHighestCard(PSTrickHash trick, PSCardSuit trump) {
    PSCard *talon = PSTrickTalonCard(trick);
    PSCard *c1 = PSTrickCard(trick, 0);
    PSCard *c2 = PSTrickCard(trick, 1);
    PSCard *c3 = PSTrickCard(trick, 2);
    // Are we in the passing mode?
    if (talon) {
        PSCardSuit primary = talon.suit;
        if (c3) {
            return PSTrickHighestWithTrump(PSTrickHighestWithTrump(c1, c2, primary), c3, primary);
        }
        if (c2) {
            return c1.suit == primary || c2.suit == primary ? PSTrickHighestWithTrump(c1, c2, primary) : nil;
        }
        return c1 && c1.suit == primary ? c1 : nil;
    }
    // No - check the other three cards
    if (c3) {
        return PSTrickHighestWithTrump(PSTrickHighestWithTrump(c1, c2, trump), c3, trump);
    }
    if (c2) {
        return PSTrickHighestWithTrump(c1, c2, trump);
    }
    return c1;
}

int PSTrickWinner(PSTrickHash trick, PSCardSuit trump) {
    PSCard *c = PSTrickHighestCard(trick, trump);
    if (c == PSTrickCard(trick, 0)) {
        return PSTrickCardOwner(trick, 0);
    }
    if (c == PSTrickCard(trick, 1)) {
        return PSTrickCardOwner(trick, 1);
    }
    if (c == PSTrickCard(trick, 2)) {
        return PSTrickCardOwner(trick, 2);
    }
    return -1;
}

NSString *PSTrickDescription(PSTrickHash trick, PSCardSuit trump) {
    NSMutableString *res = [NSMutableString string];
    PSCard *talon = PSTrickTalonCard(trick);
    if (talon) {
        [res appendFormat:@"(%@)", talon];
    }
    uint8_t c1 = PSTrickCardRep(trick, 0)
          , c2 = PSTrickCardRep(trick, 1)
          , c3 = PSTrickCardRep(trick, 2);
    PSTrickAppendCard(res, c1);
    [res appendString:@" "];
    PSTrickAppendCard(res, c2);
    [res appendString:@" "];
    PSTrickAppendCard(res, c3);
    if (c1 && c2 && c3) {
        [res appendFormat:@" w=%d", PSTrickWinner(trick, trump)];
    }
    return res;
}
